/**
 * 
 */
package com.explgo.backend.jpa;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import com.explgo.backend.domain.City;
import com.explgo.backend.domain.Comment;
import com.explgo.backend.domain.Place;
import com.explgo.backend.domain.Plan;
import com.explgo.backend.domain.PlanDay;
import com.explgo.backend.domain.TimeFrame;
import com.explgo.backend.domain.User;

/**
 * This class populates the database with test data. It uses Spring test
 * infrastructure to initiate the sprint context.
 * 
 * Run this test like regular unit test.
 * 
 * @author Jialiang(Cain) Wang
 * 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring/applicationContext.xml")
public class SampleDBInit {
	public static final String PLAN_NAME = "test_plan_name";

	public static final String DAY_NAME = "test_day_name";

	public static final String PLACE_NAME = "test_place_name";

	public static final String PLACE_ADDRESS = "1234 test road, test city, test state";

	public static final String COMMENT_CONTENT = "This plan is interesting.";

	public static final int NUMBER_OF_DAYS = 3;

	public static final int NUMBER_OF_PLACES = 2;

	public static final int NUMBER_OF_TIMEFRAMES = 4;

	@Autowired
	private UserRepository userDao;

	@Autowired
	private PlanRepository planDao;

	@Autowired
	private CityRepository cityDao;

	@Autowired
	private CommentRepository commentDao;

	/**
	 * Populate database with test data.
	 * */
	@Test
	@Rollback(false)
	public void init() {
		insertTestData();
	}

	/**
	 * Inserts test data.
	 * */
	@Transactional
	private void insertTestData() {
		User user = addUser();
		City city = addCity();

		Plan plan = addPlan(user, city);

		addComments(plan, user);
	}

	/**
	 * Saves the test user.
	 * */
	private User addUser() {
		User user = createUser();
		return userDao.save(user);
	}

	/**
	 * Saves the test city.
	 * */
	private City addCity() {
		City city = createCity();
		return cityDao.save(city);
	}

	/**
	 * Saves the test plan.
	 * */
	private Plan addPlan(User user, City city) {
		Plan plan = createPlan(user, city);
		return planDao.save(plan);
	}

	/**
	 * Creates test user.
	 * */
	public static User createUser() {
		User user = new User();
		user.setUsername("test");
		user.setPassword("test");
		user.setEmail("test@test.com");

		return user;
	}

	/**
	 * Creates test plan that's associated with specified user and city.
	 * */
	public static Plan createPlan(User user, City city) {
		Plan plan = newPlan();
		plan.setAuthor(user);
		plan.setCity(city);

		return plan;
	}

	/**
	 * Creates new test plan.
	 * */
	public static Plan newPlan() {
		Plan plan = new Plan();
		plan.setName(PLAN_NAME);

		for (int i = 0; i < NUMBER_OF_DAYS; i++) {
			plan.getDays().add(createPlanDay());
		}

		return plan;
	}

	/**
	 * Creates test city.
	 * */
	public static City createCity() {
		City city = new City();
		city.setName("San Francisco");

		return city;
	}

	/**
	 * Creates a test plan day.
	 * */
	private static PlanDay createPlanDay() {
		PlanDay day = new PlanDay();
		day.setName(DAY_NAME);

		for (int i = 0; i < NUMBER_OF_PLACES; i++) {
			Place place = createPlace();

			for (TimeFrame timeFrame : day.getTimeFrames()) {
				timeFrame.getPlaces().add(place);
			}
		}

		return day;
	}

	/**
	 * Creates a test plan place.
	 * */
	private static Place createPlace() {
		Place place = new Place();

		place.setName(PLACE_NAME);
		place.setAddress(PLACE_ADDRESS);

		return place;
	}

	/**
	 * Creates comments
	 * */
	private void addComments(Plan plan, User user) {
		for (int i = 0; i < 3; i++) {
			Comment comment = createComment(plan, user, COMMENT_CONTENT,
					i % 2 == 0);
			commentDao.save(comment);
		}
	}

	/**
	 * Createa a comment.
	 * */
	private static Comment createComment(Plan plan, User user, String content,
			boolean likePlan) {
		Comment comment = new Comment();
		comment.setPlan(plan);
		comment.setUser(user);
		comment.setContent(content);
		comment.setLikeThisPlan(likePlan);

		return comment;
	}
}
